#!/usr/bin/env python

import re
from urllib import request, parse
from xml.etree import ElementTree as etree

BASEURL   = 'https://maps.googleapis.com/maps/api/'
TIMESTAMP = 325962000
XMLRESULT = '<result><type>query</type><geometry><location>\
<lat>%s</lat><lng>%s</lng></location></geometry></result>'

def _google_(api, query, split=''):
    url = BASEURL + api +'/xml?%s' %parse.urlencode(query)
    xml = request.urlopen(url).read().decode('utf-8')
    return etree.fromstring(re.sub(re.compile(r'\n(\s+)?'), split, xml))

class LocationInfo(etree.Element):
    def __init__(self, query, elevation, timezone,
                 sensor, timestamp, interact):
        super().__init__('GoogleApis')
        sensor = str(sensor).lower()
        query['sensor'] = sensor
        response = _google_('geocode',  query)

        if response.find('status').text == 'ZERO_RESULTS':
            try: # insert query as result if none exist
                lat, lng = query['location'].split(',')[:2]
                response.append(etree.fromstring(XMLRESULT %(lat, lng)))
            except Exception as err:
                raise RuntimeError('query returned zero results!')

        # expects interact to be a function
        # returning a list of elements to remove
        results = response.findall('result')
        if interact is not None:
            for result in interact(results):
                response.remove(result)

        index = 0
        locations = []
        for result in response.findall('result'):
            result.set('id', str(index))
            index += 1

            if elevation or timezone:
                lat = result.find('geometry/location/lat').text
                lng = result.find('geometry/location/lng').text
                locations.append(lat +','+ lng)

        self.append(response)

        if elevation:
            query = {'sensor'    : sensor,
                     'locations' : '|'.join(locations)}
            response = _google_('elevation',  query)

            index = 0
            for result in response.findall('result'):
                result.set('id', str(index))
                index += 1

        self.append(response)

        if timezone:
            index = 0
            query = {'sensor'    : sensor,
                     'timestamp' : str(timestamp)}
            for location in locations:
                query['location'] = location
                response = _google_('timezone', query)
                response.set('id', str(index))
                self.append(response)
                index += 1

    @classmethod
    def fromcoordinates(cls, latitude, longitude, elevation=False,
                        timezone=False, sensor=False,
                        timestamp=TIMESTAMP, interact=None):
        query  = {'location' : str(latitude) +','+ str(longitude)}
        return cls(query, elevation, timezone, sensor, timestamp, interact)

    @classmethod
    def fromaddress(cls, address, elevation=False, timezone=False,
                    sensor=False, timestamp=TIMESTAMP, interact=None):
        query  = {'address' : address}
        return cls(query, elevation, timezone, sensor, timestamp, interact)

    def __len__(self):
        return len(self.findall('GeocodeResponse/result'))
    def __str__(self):
        return str(etree.tostring(self).decode('utf-8'))

    def address(self, index=0):
        address = self.find('GeocodeResponse/result[@id="'+
                            str(index) +'"]/formatted_address')
        if address is not None:
            address = address.text
        return address

    def coordinates(self, index=0):
        result = 'result[@id="'+str(index)+'"]'
        geocode = 'GeocodeResponse/'+result+'/geometry/location'
        elevation = 'ElevationResponse/'+result+'/elevation'
        try:
            latitude  = float(self.find(geocode +'/lat').text)
            longitude = float(self.find(geocode +'/lng').text)
            try:
                elevation = float(self.find(elevation).text)
                return (latitude, longitude, elevation)
            except AttributeError:
                return (latitude, longitude)
        except AttributeError:
            return None

    def timezone(self, index=0):
        timezone = 'TimeZoneResponse[@id="'+str(index)+'"]/time_zone_id'
        try: return self.find(timezone).text
        except AttributeError: return None


def single_choice(results):
    if len(results) < 2:
        return []

    print('multiple results to query! please choose one:')
    index = 1
    for result in results:
        print('', index, ":", result.find('formatted_address').text)
        index += 1

    user = input('location $ ')
    results.remove(results[int(user) - 1])
    return results


if __name__ == '__main__':
    #location = LocationInfo.fromaddress('galgebakken,torv',
    location = LocationInfo.fromcoordinates(55.678873 , 12.347663,
                            elevation=True, timezone=True,
                            interact=single_choice)
    print(str(location), '\n')
    print(len(location), location.coordinates(), location.timezone())
    print(location.address())
